iT邦幫忙

2026 iThome 鐵人賽

DAY 20
0
Vibe Coding

從 Vibe Coding 到系統架構:Gemini x Claude Code 雙 AI 協同開發 Godot 2D Roguelike 卡牌遊戲實戰系列 第 20 篇

Day 20:【真人測試】同一顆按鈕,兩次「看得到卻按不到」的原因完全不同

  • 分享至 

  • xImage
  •  

美術跟打磨告一段落後,我開始把遊戲拿給真人試玩。這次記錄到三件非常值得分享的事:

  1. 第一次玩的人一句話,讓我重新檢查整個獎勵畫面的回饋。
  2. iPhone 坑一:按鈕沒壞,但觸控被系統偷偷拿走了。
  3. iPhone 坑二:同一顆按鈕,第二次又按不到了,但原因完全不同。

這三件事有一個共同點:全部都不是坐在電腦前看程式碼就能找到的。

一、「我自己玩也不容易看到變化」

我把遊戲給家人試玩,得到的反饋非常直接:

「看不懂在玩什麼,點了卡片也沒特別注意到有什麼變化。」

當時旁邊還有一句話,我覺得非常關鍵:

「我自己玩不容易看到變化,是因為我是設計者,我知道會發生什麼事。」

這句話提醒了我:設計者腦中早就有一張「答案表」。 我知道現在是第幾關、剛剛點了哪張卡、點完牌組會增加。但第一次玩的人完全沒有這些背景資訊。

回去追程式碼才發現,這真的是字面上的事實:

# 原有邏輯:選完卡片後立即生成新獎勵並清除舊節點
func _on_card_selected(card):
    _apply_reward(card)
    _generate_rewards() # 內部直接對 card_row 執行清空與重新渲染

點擊卡片後,舊卡片被瞬間清空。雖然 Card.tscn 定義了按壓深色狀態,但該畫面狀態連一幀(Frame)都未曾繪製出來,即被新的卡片列覆蓋。

當時畫面的即時變化如下:

  • 關卡標籤:第 1 關 → 第 2 關
  • 狀態列牌組:4 張 → 5 張
  • 提示列:剩餘 9 個 → 8 個
  • 卡片區域:三張舊卡片瞬間替換為三張新卡片

資料邏輯完全正確,但變太快了!三個數字各動一下,整排卡片直接換掉,玩家根本不知道自己剛才做了什麼。

解決方案:視覺行為三階段拆解

不修改底層資料邏輯,改將「選卡」的視覺表現拆分為三個連續階段:

  1. 定格與強調:選中卡片放大並定格,明確傳遞「已選擇此卡」的訊號。
  2. 位移動效:卡片縮小並繪製飛向頂部牌組計數器的軌跡,傳遞「納入牌組」的意象。
  3. 計數器觸發:卡片抵達後,牌組張數數字更新,並觸發微小的脈衝(Pulse)動畫。

把瞬間完成的資料更新,變成眼睛看得懂的過程,玩家才能真正明白發生了什麼事。

二、iPhone 坑一:按鈕沒壞,是觸控被系統拿走了

美術做完後收到一個回報:最下面的「結束回合」按鈕在 iPhone 上按不到。

iPhone 底部有一條系統的 Home Indicator(小白條)。如果按鈕貼得太近,玩家雖然看得見按鈕,但手指點下去時,觸控事件直接被 iPhone 系統拿去處理手勢了,根本沒有傳進遊戲裡。

所以按鈕本身沒壞,程式也沒有報錯,只是事件根本沒進來。

戰鬥勝利結算畫面,結束回合與重新開始兩顆按鈕緊貼著畫面最下緣,下方金色橫線是 iPhone 的 Home Indicator(Siri 橫條),兩者幾乎沒有間距

第一版修法(搞砸了)

  • 初始方案(動態計算):在 Runtime 動態查詢系統的安全區域(Safe Area)高度,並換算為遊戲座標,動態加算至場景下緣 Margin。

  • 發生的異常:iOS Web 端的瀏覽器 API 未正確實作該查詢函式,回傳了單位不符的數值,再加上原場景已存在固定 Margin,導致兩重計數重複,畫面下緣異常空出 60% 的空白。

最終處置

回歸數據量測後發現,在目標部署的手機型號與解析度下,系統要求的安全留白高度計算結果均為固定值 44px。
與其維持逾百行具有相容性風險的動態計算程式碼,不如直接收斂系統複雜度:

  • 場景下緣留白:固定設定為 44px。
  • 戰鬥日誌高度:由 5 行微調為 4 行,移出空間給按鈕區塊。

將動態判斷改為乾淨的靜態佈局,成功解決了觸控攔截問題。

三、iPhone 坑二:Standalone 模式下旋轉螢幕的座標位移

當 Home Indicator 修正後,又收到第二次回報:在 iPhone 橫向模式下(由主畫面 PWA/Standalone 模式開啟),「結束回合」按鈕再度無法點擊。

ps: 主畫面 PWA/Standalone 模式,就是把網頁透過safari 加到螢幕主畫面。

排查過程:由猜測轉為資料驅動

  1. 猜測一(版面推擠):懷疑戰鬥日誌撐高版面將按鈕擠出。量測 Button 實體座標,位置完全正常 ➔ 排除。
  2. 猜測二(節點遮擋):懷疑 Boss 立繪或 UI 遮擋層吃掉 Touch 事件。掃描重疊節點的 mouse_filter 屬性 ➔ 排除。
  3. 數據驗證(實機 Overlay 偵測):直接在實機畫面上繪製即時 Touch 座標與 Hitbox 區域。

數據分析結果

iPhone 橫向 standalone 模式下的除錯覆蓋層,rect 顯示 x0 y62,粉紅圓圈標記的觸控點落在結束回合按鈕上,但換算出的遊戲座標 546 落在按鈕的有效範圍 632 到 676 之外

在 iOS 橫向 PWA 模式下,iOS 傳入 Web 容器的觸控座標整體向上偏移,偏移量精確等於 iOS 狀態列(Status Bar)高度(62px)。

  • 視覺呈現:按鈕位於下方。
  • 引擎判斷:玩家點擊按鈕時,引擎接收到的轉換座標落在按鈕上方 62px 處(剛好觸發了上方的卡片區域)。
真實點擊位置:[結束回合按鈕 (Y: 632~676)]
引擎接收座標:[Y: 570] ➔ 誤判定為點擊卡片區域

同一支 iPhone、同樣橫向 standalone 模式,rect 顯示 x0 y0,觸控點正確落在結束回合按鈕的有效範圍內

當系統的 rect 恢復為 x0 y0 時,觸控輸入即完全恢復正常。

成因與傷害控制(Damage Control)

成因:iOS 在裝置旋轉時,Web 容器未正確觸發排版重繪(Reflow),直向模式時保留的頂部 Status Bar Padding 未被清理,導致 Touch 座標映射出現整體偏移。

處置方案:經多次嘗試強制排版,部分 iOS 版本仍會在連續旋轉後再現該 Bug。最終採取風險控制策略:當偵測至橫向 PWA 模式且座標異常時,跳出提示引導玩家重新以橫向開啟應用程式,避開該系統層級的運算異常路徑。

四、實機測試的工程反思

  1. 你以為很明顯,是因為你早就知道答案。
    程式沒有報錯、邏輯寫得完全正確,只代表「系統運作正常」,不代表「玩家眼睛看得懂」。畫面必須留時間讓玩家的眼睛反應過來。

  2. 不要為了假裝聰明,把簡單的事情搞複雜。
    寫了一百行看似很酷的動態計算程式,結果在各種手機上出錯。既然答案永遠是 44,把那一百行刪掉直接寫 44 就好了。

  3. 別用猜的!查看實際數據。
    兩次都是「按鈕看得到按不到」,一次是被 iPhone 底部的橫條擋住,一次是轉向後座標往上偏。症狀一樣,不代表是同一種病。沒印出數據之前,不要憑感覺瞎猜。

  4. 不是每個坑都能修到底,誠實記錄修到哪裡也是一種交代。
    轉向座標偏移最終是靠提示繞過,不是徹底解決,這篇沒有把它包裝成一個乾淨的結局。

下一步:Day 21
完成實機輸入與結構排查後,下一步將聚焦於出牌打擊感與戰鬥視覺回饋(Juiciness)的打磨:優化傷害飄字、打擊停頓與視覺震動,提升卡牌對戰的物理回饋感,讓玩家出牌時不只是數字變了,而是真的感覺到「我打到了!」。


上一篇
Day 19:【部署驗證】Godot 跨平台實機驗證:Web、Android、iOS 部署流程與六個踩坑紀錄
下一篇
Day 21:【動畫打磨】讓出牌有「打到了」的手感
系列文
從 Vibe Coding 到系統架構:Gemini x Claude Code 雙 AI 協同開發 Godot 2D Roguelike 卡牌遊戲實戰 共 26 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言